/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.jm.mala.generic.impl;

import br.jm.mala.generic.DAOGeneric;
import br.jm.mala.util.Configuracao;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.hibernate.CacheMode;

/**
 *
 * @author joao
 * @param <T>
 */
public abstract class DAOGenericImpl<T extends Serializable> implements Serializable, DAOGeneric<T> {

    private static final long serialVersionUID = 8940386195719758487L;

    @PersistenceContext
    private EntityManager entityManager;

    private final Class<?> tipo;
    private Map tabela;
    private Map tabelaType;
    private String cOrder;
    private String cOrderDirection;
    private String type;
    private String cCampoTabela;

    @SuppressWarnings("unchecked")
    public DAOGenericImpl() {
        tipo = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    @SuppressWarnings("rawtypes")
    @Override
    public T login(String username, String password) {
        String query = "FROM User u where u.username = :username and u.password= :password";
        Query q = entityManager.createQuery(query);
        q.setParameter("username", username);
        q.setParameter("password", password);

        List l = q.getResultList();

        if (l == null || l.isEmpty() || l.size() > 1) {
            return null;
        }
        return (T) l.get(0);
    }

    @Override
    public void create(T entidade) {
        entityManager.persist(entidade);
    }

    @Override
    public void edit(T entidade) {
        entityManager.merge(entidade);
    }

    @Override
    public void destroy(T entidade) {
        T _Entidade = entityManager.merge(entidade);
        entityManager.remove(_Entidade);
    }

    @Override
    public final void executeUpdate(String oql) {
        Query query = entityManager.createNativeQuery(oql);
        query.executeUpdate();

    }

    @SuppressWarnings("unchecked")
    @Override
    public T getPorId(Object id) {
        return (T) entityManager.find(this.tipo, id);
    }

    // Consultada manuais dinamicas
    @SuppressWarnings(value = "unchecked")
    @Override
    public List<T> get(String oql) {
        Query q = entityManager.createQuery(oql);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        List<T> colecao = q.getResultList();
        return colecao;
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public List<T> get(String oql, List<String> campos) {
        Query q = entityManager.createQuery(oql);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        for (int ct = 0; ct < campos.size(); ++ct) {
            q.setParameter(ct + 1, campos.get(ct));
        }
        List<T> colecao = q.getResultList();
        return colecao;
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public List<T> get(String oql, Object... campos) {
        Query q = entityManager.createQuery(oql);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        for (int ct = 0; ct < campos.length; ++ct) {
            q.setParameter(ct + 1, campos[ct]);
        }
        List<T> colecao = q.getResultList();
        return colecao;
    }

    // Consultada com query estatucas
    @SuppressWarnings(value = "unchecked")
    @Override
    public List<T> getPorQuery(String query) {
        Query q = entityManager.createNamedQuery(query);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        List<T> colecao = q.getResultList();
        return colecao;
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public List<T> getPorQuery(String query, Object... campos) {
        Query q = entityManager.createNamedQuery(query);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        for (int ct = 0; ct < campos.length; ++ct) {
            q.setParameter(ct + 1, campos[ct]);
        }
        List<T> colecao = q.getResultList();
        return colecao;
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public int getCount(String entidade) {
        Query q = entityManager.createQuery("select count(o) from " + entidade + " as o");
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        return ((Long) q.getSingleResult()).intValue();
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public int getCount(String entidade, String where) {
        Query q = entityManager.createQuery("select count(o) from " + entidade + " as o ".concat(where));
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        return ((Long) q.getSingleResult()).intValue();
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public int getCountQuery(String oql) {
        Query q = entityManager.createQuery(oql);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        return ((Long) q.getSingleResult()).intValue();
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public List<T> getNative(String cTabela, int first, int pageSize, String sortField, boolean sortOrder, Map<String, String> filters, String cParametroSQL) {
        List<T> colecao;

        colecao = getNative(cTabela, first, pageSize, sortField, sortOrder, filters, cParametroSQL, null);
        return colecao;
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public List<T> getNative(String cTabela, int first, int pageSize, String sortField, boolean sortOrder, Map<String, String> filters, String cParametroSQL, String cFiltro) {
        String cSql;

        if (cParametroSQL != null) {
            cSql = cParametroSQL;
        } else {
            tabela = Configuracao.tabelasMap.get(cTabela);
            tabelaType = Configuracao.tabelasMap.get(cTabela.concat("Type"));

            cSql = tabela.get(cTabela).toString();

            if (cFiltro != null) {
                if (cSql.toLowerCase().contains("where")) {
                    cSql += " and ";
                } else {
                    cSql += " where ";
                }

                cSql += cFiltro;
            }

            if (filters != null) {
                if (!filters.isEmpty()) {
                    Set<String> chaves = filters.keySet();
                    for (String chave : chaves) {
                        type = (String) tabelaType.get(chave);
                        cCampoTabela = tabela.get(chave).toString();

                        if (cSql.toLowerCase().contains("where")) {
                            cSql += " and ";
                        } else {
                            cSql += " where ";
                        }

                        if (type.toLowerCase().contains("string")) {
                            cSql += cCampoTabela.concat(" like '%").concat(filters.get(chave)).concat("%' collate Latin1_General_CI_AI ");
                        } else if (type.toLowerCase().contains("date")) {
                            cSql += "convert(varchar(10), ".concat(cCampoTabela).concat(", 103) like '%").concat(filters.get(chave)).concat("%' ");
                        } else if (type.toLowerCase().contains("integer") || type.toLowerCase().contains("int")) {
                            cSql += cCampoTabela.concat(" = ").concat(filters.get(chave)).concat(" ");
                        } else if (type.toLowerCase().contains("bigdecimal")) {
                            cSql += "convert(decimal(18,2), ".concat(cCampoTabela).concat(" ) like '%").concat(filters.get(chave)).concat("%' ");
                        }
                    }
                }
            }

            String cPaginacao = " ) select * from paginacao "
                    + "where rownumber between ( 1 + par3 ) and ( 1 + par3 + par4 ) ";

            cSql += cPaginacao;

            if (sortField != null) {
                cOrder = (String) tabela.get(sortField);
            } else {
                cOrder = (String) tabela.get("Order");
            }

            if (sortOrder) {
                cOrderDirection = "asc";
            } else {
                cOrderDirection = "desc";
            }

            cSql = cSql.replace("par1", cOrder);
            cSql = cSql.replace("par2", cOrderDirection);
            cSql = cSql.replace("par3", Integer.toString(first));
            cSql = cSql.replace("par4", Integer.toString(pageSize));
        }

        Query q = entityManager.createNativeQuery(cSql, this.tipo);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        List<T> colecao = q.getResultList();
        return colecao;
    }

    @SuppressWarnings(value = "unchecked")
    @Override
    public List<T> getNativeFiltroList(String cSql) {
        Query q = entityManager.createNativeQuery(cSql, this.tipo);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        List<T> colecao = q.getResultList();
        return colecao;
    }
    
    @SuppressWarnings(value = "unchecked")
    @Override
    public T getNativeFiltro(String cSql) {
        Query q = entityManager.createNativeQuery(cSql, this.tipo);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        T colecao = (T) q.getResultList().get(0);
        return colecao;
    }
    
    // Consultada manuais dinamicas
    @SuppressWarnings(value = "unchecked")
    @Override
    public T getEntidade(String oql, List<String> campos) {
        Query q = entityManager.createQuery(oql);
        q.setHint("org.hibernate.cacheMode", CacheMode.REFRESH);
        q.setHint("org.hibernate.cacheable", true);
        for (int ct = 0; ct < campos.size(); ++ct) {
            q.setParameter(ct + 1, campos.get(ct));
        }
        T colecao = (T) q.getSingleResult();
        return colecao;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<T> loadAll() {
        return entityManager.createQuery("Select t from " + tipo.getSimpleName() + " t").getResultList();
    }
}
